public class NationalAccountEvents {
    
    private static AgencyClientNumberGenerator clientNumberGenerator;
    private static List<Account> QueueCmrForNasaIDRefresh = new List<Account>();
    private static List<Account> QueueAasForNasaIDRefresh = new List<Account>();
    private static List<Account> QueueForActiveChildrenCheck = new List<Account>();
    private static List<Account> QueueForInactiveParentCheck = new List<Account>();
    private static List<Account> QueueForNonCmrGroupParentCheck = new List<Account>();
            
    public static void OnAccountBeforeInsert(List<Account> oldAcc, List<Account> newAcc ){

        ID aasMasterClientRT = RecordTypeHelper.GetRecordTypeIDByDevName('Account', 'AAS_Master_Client');
        ID cmrGroupRT = RecordTypeHelper.GetRecordTypeIDByDevName('Account', 'CMR_Group');   
        ID aasAgencyRT = RecordTypeHelper.GetRecordTypeIDByDevName('Account', 'AAS_Agency_Account');
        ID aasSalesAccountRT = RecordTypeHelper.GetRecordTypeIDByDevName('Account', 'Advanced_Ad_Solutions');

        ID nsMasterClientRT = RecordTypeHelper.GetRecordTypeIDByDevName('Account', 'NS_Master_Client');
        ID cmrAgencyRT = RecordTypeHelper.GetRecordTypeIDByDevName('Account', 'CMR_Agency');        
        ID cmrAgencyClient = RecordTypeHelper.GetRecordTypeIDByDevName('Account', 'CMR_Agency_Client');

        ID ingenioManagedCustomer = RecordTypeHelper.GetRecordTypeIDByDevName('Account', 'National_Managed_Customer_Ingenio');
        ID afrontierManagedCustomer = RecordTypeHelper.GetRecordTypeIDByDevName('Account', 'CMR_Agency_Managed');
        
        AutoNumberHelper masterClientAutoNumber = new AutoNumberHelper(AutoNumberHelper.AutoNumberType.AccountMasterClientID);      
        AutoNumberHelper agencyAutoNumber = new AutoNumberHelper(AutoNumberHelper.AutoNumberType.AccountAgencyID);      
                            
        for(Account a : newAcc){
            // AAS Master Client
            if(a.RecordTypeId == aasMasterClientRT){
                PerformValidationForMasterClient(null, a);
                masterClientAutoNumber.PrepareAutoNumber(a);
            }
            
            // NS (CMR) Master Client
            if(a.RecordTypeId == nsMasterClientRT){
                PerformValidationForMasterClient(null, a);
                masterClientAutoNumber.PrepareAutoNumber(a);
            }

            // NS (CMR) Group
            if(a.RecordTypeId == cmrGroupRT){
                PerformValidationForGroup(null, a);
                PopulateGroupNasaId(a, true);
            }
            
            // AAS Agency
            if(a.RecordTypeId == aasAgencyRT){
                PerformValidationForAgency(null, a, false);
                PopulateAgencyId(agencyAutoNumber, null, a, false, false);
            }
            
            // CMR Agency
            if(a.RecordTypeId == cmrAgencyRT){
                PerformValidationForAgency(null, a, true);
                PopulateAgencyId(agencyAutoNumber, null, a, true, false);                
            }
                        
            // AAS Sales Account
            if(a.RecordTypeId == aasSalesAccountRT){
                PerformValidationForAgencyClient(null, a, false, false, false);
                PopulateAgencyClientNasaId(null, a, false, false, false);
            }
            
            // CMR Agency Client
            if(a.RecordTypeId == cmrAgencyClient){
                PerformValidationForAgencyClient(null, a, true, false, false);
                PopulateAgencyClientNasaId(null, a, true, false, false);
                DuplicateAgencyInformation(null, a, true);
            }
            
            // Managed Customers
            if(a.RecordTypeId == ingenioManagedCustomer || a.RecordTypeId == afrontierManagedCustomer){
                a.Agency_Client__c = a.ParentID;
            }
            
        }
        
        CheckForNonCmrGroupParent();           
        CheckForActiveChildren();
        CheckForInActiveParent();
            
        agencyAutoNumber.Populate();                
        for(sObject o : agencyAutoNumber.GetPopulatedObjects()){
            Account a = (Account)o;
            // AAS Agency
            if(a.RecordTypeId == aasAgencyRT){
                a.AAS_Agency_ID__c = a.Agency_ID__c;
            }
            
            // CMR Agency
            if(a.RecordTypeId == cmrAgencyRT){
                //a.CMR_ID__c = a.Agency_ID__c;
                System.Assert(false, 'We should not be auto populating CMR Agency IDs');
            }
            a.NASA_ID__c = a.Agency_ID__c;
        }

        masterClientAutoNumber.Populate();
        for(sObject o : masterClientAutoNumber.GetPopulatedObjects()){
            Account a = (Account)o;
            a.NASA_ID__c = a.Master_Client_ID__c;
        }
        
        if(null != clientNumberGenerator)
            clientNumberGenerator.GenerateClientNumbers();
                    
        RefreshNasaIDs(QueueCmrForNasaIDRefresh, true);
        RefreshNasaIDs(QueueAasForNasaIDRefresh, false);
    }
    
    public static void OnAccountBeforeUpdate(List<Account> oldAcc, List<Account> newAcc ){
        ID aasMasterClientRT = RecordTypeHelper.GetRecordTypeIDByDevName('Account', 'AAS_Master_Client');
        ID aasAgencyRT = RecordTypeHelper.GetRecordTypeIDByDevName('Account', 'AAS_Agency_Account');
        ID aasSalesAccountRT = RecordTypeHelper.GetRecordTypeIDByDevName('Account', 'Advanced_Ad_Solutions');

        ID nsMasterClientRT = RecordTypeHelper.GetRecordTypeIDByDevName('Account', 'NS_Master_Client');
        ID cmrGroupRT = RecordTypeHelper.GetRecordTypeIDByDevName('Account', 'CMR_Group');   
        ID cmrAgencyRT = RecordTypeHelper.GetRecordTypeIDByDevName('Account', 'CMR_Agency');
        ID cmrAgencyClient = RecordTypeHelper.GetRecordTypeIDByDevName('Account', 'CMR_Agency_Client');
                        
        ID ingenioManagedCustomer = RecordTypeHelper.GetRecordTypeIDByDevName('Account', 'National_Managed_Customer_Ingenio');
        ID afrontierManagedCustomer = RecordTypeHelper.GetRecordTypeIDByDevName('Account', 'CMR_Agency_Managed');
        
        AutoNumberHelper agencyAutoNumber = new AutoNumberHelper(AutoNumberHelper.AutoNumberType.AccountAgencyID);  
        User loginUser = getLoginUser();
        Boolean isNSAssignmentManagerUser = loginUser != null && loginUser.Profile.Name == 'National Sales Assignment Manager';
        Boolean isAASadminOrOpsTeamUser = loginUser != null && (loginUser.Profile.Name == 'Advanced Ad Solutions (Delegated Admin)' || loginUser.Profile.Name == 'AAS Operations Team');
        	            
        Map<ID, Account> accountsWithClosedOpps = getAccsWithClosedOpps(newAcc);
           
        for(integer i=0; i < newAcc.size(); i++)
        {
            // AAS Master Client || NS (CMR) Master Client
            if(newAcc[i].RecordTypeId == aasMasterClientRT || newAcc[i].RecordTypeId == nsMasterClientRT){
                PerformValidationForMasterClient(oldacc[i], newacc[i]);             
            }

            // NS (CMR) Group
            if(newAcc[i].RecordTypeId == cmrGroupRT){
                PerformValidationForGroup(oldacc[i], newacc[i]);
                PopulateGroupNasaId(newAcc[i], true);
            }

            // AAS Agency
            if(newAcc[i].RecordTypeId == aasAgencyRT){
                PerformValidationForAgency(oldacc[i], newacc[i], false);               
                PopulateAgencyId(agencyAutoNumber, oldAcc[i], newAcc[i], false, false);
            }
            
            // CMR Agency
            if(newAcc[i].RecordTypeId == cmrAgencyRT){
            	PerformValidationForAgency(oldacc[i], newacc[i], true);                               
                PopulateAgencyId(agencyAutoNumber, oldAcc[i], newAcc[i], true, isNSAssignmentManagerUser);
            }

            // AAS Sales Account or CMR Agency Client
            if(newAcc[i].RecordTypeId == aasSalesAccountRT || newAcc[i].RecordTypeId == cmrAgencyClient){
            	Boolean hasNoClosedOpportunities = accountsWithClosedOpps != null && accountsWithClosedOpps.get(newacc[i].Id).opportunities.isEmpty() && newacc[i].Account_Locked__c == false; 
                PerformValidationForAgencyClient(oldacc[i], newacc[i], newAcc[i].RecordTypeId == cmrAgencyClient, isAASadminOrOpsTeamUser, hasNoClosedOpportunities);              
                PopulateAgencyClientNasaId(oldAcc[i], newAcc[i], newAcc[i].RecordTypeId == cmrAgencyClient, isAASadminOrOpsTeamUser, hasNoClosedOpportunities);
                DuplicateAgencyInformation(oldAcc[i], newAcc[i], newAcc[i].RecordTypeId == cmrAgencyClient);
            }           

            // Managed Customers
            if(newAcc[i].RecordTypeId == ingenioManagedCustomer || newAcc[i].RecordTypeId == afrontierManagedCustomer){
                if(newAcc[i].ParentId != oldAcc[i].ParentId){
                    newAcc[i].Agency_Client__c = newAcc[i].ParentId;
                }
            }
          
        }           
        
        CheckForNonCmrGroupParent();
        CheckForActiveChildren();
        CheckForInActiveParent();
        
        agencyAutoNumber.Populate();
        
        for(sObject o : agencyAutoNumber.GetPopulatedObjects()){
            Account a = (Account)o;
            // AAS Agency
            if(a.RecordTypeId == aasAgencyRT){
                a.AAS_Agency_ID__c = a.Agency_ID__c;
            }
            
            // CMR Agency - We a no longer populating CMR Agency Ids
            if(a.RecordTypeId == cmrAgencyRT){
                System.Assert(false, 'We should not be auto populating CMR Agency IDs');
                //a.CMR_ID__c = a.Agency_ID__c;
            }
            a.NASA_ID__c = a.Agency_ID__c;
        }                   
        
        if(null != clientNumberGenerator)
            clientNumberGenerator.GenerateClientNumbers();
            
        RefreshNasaIDs(QueueCmrForNasaIDRefresh, true);
        RefreshNasaIDs(QueueAasForNasaIDRefresh, false);
    }
    
    private static User getLoginUser(){
    	return [select Id, Profile.Name, UserRole.Name from User where Id =: UserInfo.getUserId()];
    }
    
    private static Map<ID, Account> getAccsWithClosedOpps(List<Account> accts){
    	Set<ID> acctIds = new Set<ID>();
    	for(Account a : accts){
    		acctIds.add(a.Id);
    	}
    	return new Map<ID, Account>([select id, (select id from opportunities where Probability >= 90) from Account where Id IN: acctIds]);
    }
            
    private static void PerformValidationForMasterClient(Account oldAcc, Account newAcc){
        // Verify that fields in a non-draft record are not changing        
        if(oldAcc != null && oldAcc.Account_Status__c <> 'Draft'){        
            if(oldAcc.Account_Status__c <> 'Inactive' && newAcc.Account_Status__c == 'Draft') // sshu : AD-26890
                newAcc.Account_Status__c.AddError('You cannot revert the account status to draft mode.');
                
            if(newAcc.RecordTypeId != oldAcc.RecordTypeId)
                newAcc.RecordTypeId.AddError('You cannot change the record type of a non-draft account.');
        }
        
        // Changing account status to inactive - perform validation
        if(oldAcc != null && oldAcc.Account_Status__c <> 'Inactive' && newAcc.Account_Status__c == 'Inactive'){
            QueueForActiveChildrenCheck.add(newAcc);
        }
                            
    }

    private static void PerformValidationForGroup(Account oldAcc, Account newAcc){     
        // Verify that fields in a non-draft record are not changing        
        if(oldAcc != null && oldAcc.Account_Status__c <> 'Draft'){       
            if(oldAcc.Account_Status__c <> 'Inactive' && newAcc.Account_Status__c == 'Draft') // sshu : AD-26890
                newAcc.Account_Status__c.AddError('You cannot revert the account status to draft mode.');
                
            if(newAcc.CMR_Group_ID__c != oldAcc.CMR_Group_ID__c)
                newAcc.CMR_Group_ID__c.AddError('You cannot change the CMR Group of a non-draft account.');
                
            if(newAcc.RecordTypeId != oldAcc.RecordTypeId)
                newAcc.RecordTypeId.AddError('You cannot change the record type of a non-draft account.');
        }

        // Changing account status to inactive - perform validation
        if(oldAcc != null && oldAcc.Account_Status__c <> 'Inactive' && newAcc.Account_Status__c == 'Inactive'){
            QueueForActiveChildrenCheck.add(newAcc);
        }
        
        // If the group ID changed, verify the new value
        if(newAcc.CMR_Group_ID__c != null && (oldAcc == null || newAcc.CMR_Group_ID__c <> oldAcc.CMR_Group_ID__c)){
            if(!IsValidNumber(newAcc.CMR_Group_ID__c, 1, 4))
                newAcc.CMR_Group_ID__c.AddError('The CMR Group ID must be a value between 0 and 9999.');    
        }
    }

    private static void PerformValidationForAgency(Account oldAcc, Account newAcc, boolean isCmr){     
        // Verify that fields in a non-draft record are not changing        
        if(oldAcc != null && oldAcc.Account_Status__c <> 'Draft'){       
            if(oldAcc.Account_Status__c <> 'Inactive' && newAcc.Account_Status__c == 'Draft') // sshu : AD-26890
                newAcc.Account_Status__c.AddError('You cannot revert the account status to draft mode.');
            
            if(newAcc.RecordTypeId != oldAcc.RecordTypeId)
                newAcc.RecordTypeId.AddError('You cannot change the record type of a non-draft account.');
        }
                        
        if(isCmr && !IsValidNumber(newAcc.CMR_ID__c, 1, 6))
            newAcc.CMR_ID__c.AddError('A CMR ID is required and must be a value between 0 and 999999.');

        if(isCmr && newAcc.ParentId == null && newAcc.Account_Status__c <> 'Draft'){
            newAcc.ParentId.AddError('A CMR Group parent account is required.');
        }
        
        // Changing the parent of a CMR Agency
        if(isCmr && (oldAcc == null || newAcc.ParentId != oldAcc.ParentId))
        {
            if(newAcc.ParentId != null){
                // If the account is active, ensure the new parent is active
                if(newAcc.Account_Status__c <> 'Draft')
                    QueueForInactiveParentCheck.add(newAcc);
                
                // Always check that the new parent is a CMR Group  
                QueueForNonCmrGroupParentCheck.add(newAcc);             
            }
        }
           
        // Changing account status to inactive - perform validation
        if(oldAcc != null && oldAcc.Account_Status__c <> 'Inactive' && newAcc.Account_Status__c == 'Inactive'){
            QueueForActiveChildrenCheck.add(newAcc);
        }
        
        // Changing account status to active - perform validation
        if(oldAcc != null && oldAcc.Account_Status__c <> 'Active' && newAcc.Account_Status__c == 'Active'){
            QueueForInactiveParentCheck.add(newAcc);
        }
        
        // Changing account status to active - perform validation
        if(oldAcc != null && oldAcc.Account_Status__c <> 'Active' && newAcc.Account_Status__c == 'Active'){
            QueueForInactiveParentCheck.add(newAcc);
        }
    }
    
    private static void PerformValidationForAgencyClient(Account oldAcc, Account newAcc, boolean isCmr, Boolean isAASadminOrOpsTeam, Boolean hasNoClosedOpps){        
        // Verify that fields in a non-draft record are not changing   
        if(oldAcc != null && oldAcc.Account_Status__c <> 'Draft'){      
            if(oldAcc.Account_Status__c <> 'Inactive' && newAcc.Account_Status__c == 'Draft') // sshu : AD-26890
                newAcc.AddError('You cannot revert the account status to draft mode.');
                
            if(!isCmr && newAcc.Master_Client__c != oldAcc.Master_Client__c && (!isAASadminOrOpsTeam || !hasNoClosedOpps))
                newAcc.Master_Client__c.AddError('You cannot change the Master Client of a non-draft account.');

            if(newAcc.NASA_ID__c != oldAcc.NASA_ID__c)
                newAcc.NASA_ID__c.AddError('You cannot change the NASA ID of a non-draft account.');

            // This field is not on the page layouts - but we'll double check anyways
            if(newAcc.CMR_ClientCode__c != oldAcc.CMR_ClientCode__c)
                newAcc.AddError('You cannot change the Client Code of a non-draft account.');

            if(newAcc.Client_Number__c!= oldAcc.Client_Number__c)
                newAcc.Client_Number__c.AddError('You cannot change the Client Number of a non-draft account.');
                
            if(newAcc.AAS_Agency_Name__c!= oldAcc.AAS_Agency_Name__c && (!isAASadminOrOpsTeam || !hasNoClosedOpps))
                newAcc.AAS_Agency_Name__c.AddError('You cannot change the AAS Agency of a non-draft account.');

            if(newAcc.Agency__c!= oldAcc.Agency__c)
                newAcc.Agency__c.AddError('You cannot change the CMR of a non-draft account.');
                
            if(newAcc.RecordTypeId != oldAcc.RecordTypeId)
                newAcc.RecordTypeId.AddError('You cannot change the record type of a non-draft account.');                              
        }
        else{
            // Verify draft fields are valid
            if(isCmr){              
                if(newAcc.Client_Number__c != null){
                    newAcc.Client_Number__c = PrefixString(newAcc.Client_Number__c, '0', 4);                
                    if(newAcc.Client_Number__c.length() != 4)
                        newAcc.Client_Number__c.AddError('The client number must be four digits.');
                }
            }
        }
        
        // Verify we have all the info when moving from Draft to 'real' client
        if(newAcc.Account_Status__c <> 'Draft' && (oldAcc == null || oldAcc.Account_Status__c == 'Draft')){
            if(isCmr){              
                if(newAcc.Client_Number__c == null){
                    newAcc.Client_Number__c.AddError('A four digit client number must be provided.');
                }
            }
        }
        
        // Changing the parent of a CMR Agency
        if(isCmr && (oldAcc == null || newAcc.Master_Client__c != oldAcc.Master_Client__c))
        {
            if(newAcc.Master_Client__c != null){
                // If the account is active, ensure the new parent is active
                if(newAcc.Account_Status__c <> 'Draft')
                    QueueForInactiveParentCheck.add(newAcc);
            }
        }

        if(null != newAcc.Print_CMR_ID__c && !IsValidNumber(newAcc.Print_CMR_ID__c, 1, 4)) // Verify a 4 digit Print CMR ID
                newAcc.Print_CMR_ID__c.AddError('The Print CMR ID must be a value between 0 and 9999.');    
                                   
        if(null != newAcc.Print_Client_Code__c && !IsValidNumber(newAcc.Print_Client_Code__c, 1, 4)) // Verify a 4 digit Print Client Code
                newAcc.Print_Client_Code__c.AddError('The Print Client Code must be a value between 0 and 9999.');                  
                
        // Changing account status to active - perform validation
        if(oldAcc != null && oldAcc.Account_Status__c <> 'Active' && newAcc.Account_Status__c == 'Active'){
            QueueForInactiveParentCheck.add(newAcc);
        }else if(oldAcc != null && newAcc.Account_Status__c == 'Active' && 
                 (newAcc.Master_Client__c != oldAcc.Master_Client__c || newAcc.AAS_Agency_Name__c != oldAcc.AAS_Agency_Name__c || newAcc.Agency__c != oldAcc.Agency__c)){
                 	QueueForInactiveParentCheck.add(newAcc);
        }
        
    }                        
    
    private static void PopulateAgencyId(AutoNumberHelper anh, Account oldAgency, Account newAgency, boolean isCmr, boolean isNSAssignManager){
        
        string oldCmrId = (oldAgency == null) ? null : oldAgency.CMR_ID__c;
        string oldAccountTypeId = (oldAgency == null) ? null : oldAgency.Agency_Type__c;
        
        // If the CMR ID has changed or the Agency Type has changed
        if(newAgency.CMR_ID__c != oldCmrId || oldAccountTypeId != newAgency.Agency_Type__c)
        {
            // If it wasn't a draft then the user should not have been able to change the CMR ID!
            if(oldAgency != null && oldAgency.Account_Status__c <> 'Draft'){
            	if(newAgency.CMR_ID__c != oldCmrId){
            		newAgency.CMR_ID__c.AddError('You cannot change the CMR ID of a non-Draft account');
            	}
            	if(oldAccountTypeId != newAgency.Agency_Type__c && !isNSAssignManager){
            		newAgency.Agency_Type__c.AddError('You cannot change the Agency Type of a non-Draft account');
            	}
            }
            
            // For CMR Agencies, use the CMR ID
            if(isCmr){
                // Set the Agency ID to the CMR ID               
                newAgency.Agency_ID__c = newAgency.CMR_ID__c;
                newAgency.NASA_ID__c = newAgency.Agency_ID__c;                      
            }
            else{
                if(!IsValidNumber(newAgency.Agency_ID__c, 6, 6)){ // Verify a 6 digit auto-number
                    // Clear out the agency ID (to be populated by an auto number)
                    newAgency.Agency_ID__c = null;                      
                }
            }
        }

        if(newAgency.Agency_ID__c == null && !isCmr)
            anh.PrepareAutoNumber(newAgency);
    }
    
    private static void CheckForActiveChildren(){
        if(null != QueueForActiveChildrenCheck && QueueForActiveChildrenCheck.size() > 0){
            Set<ID> parentIDs = new Set<ID>();
            for(Account a: QueueForActiveChildrenCheck){
                parentIDs.add(a.ID);
            }
            
           //List<Account> childAcc =[Select ParentId from Account where Account_Status__c != 'Inactive' and ParentID IN :parentIDs];
        List<Account> childAcc = [Select AAS_Agency_Name__c, Agency__c, Master_Client__c, ParentID from Account where Account_Status__c != 'Inactive' and (AAS_Agency_Name__c IN :parentIDs or Agency__c in :parentIDs or Master_Client__c in :parentIDs or ParentID in :parentIDs)];
           if(childAcc.size() > 0){
                Set<ID> invalidParentIDs = new Set<ID>();
                for(Account child: childAcc){
                    if(null != child.parentID) invalidParentIDs.add(child.parentID);
                    if(null != child.AAS_Agency_Name__c) invalidParentIDs.add(child.AAS_Agency_Name__c);
                    if(null != child.Master_Client__c) invalidParentIDs.add(child.Master_Client__c);
                    if(null != child.Agency__c) invalidParentIDs.add(child.Agency__c);
                }
                for(Account a: QueueForActiveChildrenCheck){
                    if(invalidParentIDs.contains(a.ID))
                        a.Account_Status__c.addError('This account has children that are not marked as inactive. ');            
                }
           }
            
        }
    }

    private static void CheckForInActiveParent(){
        if(null != QueueForInactiveParentCheck && QueueForInactiveParentCheck.size() > 0){
            Map<ID, Account> parentIDs = new Map<ID,Account>();
            for(Account child: QueueForInactiveParentCheck){
                if(null != child.parentID) parentIDs.put(child.parentID, child);
                if(null != child.AAS_Agency_Name__c) parentIDs.put(child.AAS_Agency_Name__c, child);
                if(null != child.Master_Client__c) parentIDs.put(child.Master_Client__c, child);
                if(null != child.Agency__c) parentIDs.put(child.Agency__c, child);
            }
            
           //List<Account> childAcc =[Select ParentId from Account where Account_Status__c != 'Inactive' and ParentID IN :parentIDs];
        List<Account> parentAcc = [Select ID from Account where Account_Status__c != 'Active' and ID in :parentIDs.keyset()];
           if(parentAcc.size() > 0)
           {
                Set<ID> invalidParentIDs = new Set<ID>();
                for(Account parent: parentAcc)
                {
                    parentIDs.get(parent.ID).Account_Status__c.addError('This account has a parent account that is not marked as active.');
                }
           }
            
        }
    }

    private static void CheckForNonCmrGroupParent(){
        if(null != QueueForNonCmrGroupParentCheck && QueueForNonCmrGroupParentCheck.size() > 0){
            Map<ID, Account> parentIDs = new Map<ID,Account>();
            for(Account child: QueueForNonCmrGroupParentCheck){
                if(null != child.parentID) parentIDs.put(child.parentID, child);
            }
            
           //List<Account> childAcc =[Select ParentId from Account where Account_Status__c != 'Inactive' and ParentID IN :parentIDs];
        ID cmrGroupRT = RecordTypeHelper.GetRecordTypeIDByDevName('Account', 'CMR_Group');             
        List<Account> parentAcc = [Select ID from Account where RecordTypeId != :cmrGroupRT and ID in :parentIDs.keyset()];
           if(parentAcc.size() > 0){
                Set<ID> invalidParentIDs = new Set<ID>();
                for(Account parent: parentAcc){
                    parentIDs.get(parent.ID).ParentID.addError('The parent must be a CMR Group.');
                }
           }
            
        }
    }
    
    private static void PopulateGroupNasaId(Account a, boolean isCmr){
        System.Assert(isCmr, 'Non CMR groups are not supported');
        
        if(null != a.CMR_Group_ID__c){
            a.NASA_ID__c = 'G' + a.CMR_Group_ID__c;
        }
        else{
            a.NASA_ID__c = null;
        }
    }
    
    private static void PopulateAgencyClientNasaId(Account oldAcc, Account newAcc, boolean isCmr, Boolean isAASadminOrOpsTeam, Boolean hasNoClosedOpps){
                                
        if(oldAcc == null || oldAcc.Account_Status__c == 'Draft' || (isAASadminOrOpsTeam && hasNoClosedOpps)){
            
            if(newAcc.Client_Number__c == null || (oldAcc != null && newAcc.Master_Client__c != oldAcc.Master_Client__c)){
                if(null == clientNumberGenerator)
                    clientNumberGenerator = new AgencyClientNumberGenerator();
                                                
                if(!isCmr) // Only generate Client Numbers for AAS Sales Account
                    clientNumberGenerator.QueueForClientNumberGeneration(newAcc);
            }
            
            if(newAcc.Client_Number__c == null || 
            oldAcc == null || 
            newAcc.Client_Number__c != oldAcc.Client_Number__c || 
            newAcc.Master_Client__c != oldAcc.Master_Client__c ||
            newAcc.AAS_Agency_Name__c != oldAcc.AAS_Agency_Name__c || 
            newAcc.Agency__c != oldAcc.Agency__c){
                if(isCmr)
                    QueueCmrForNasaIDRefresh.add(newAcc);
                else
                    QueueAasForNasaIDRefresh.add(newAcc);
            }
        }       
    }

    private static void DuplicateAgencyInformation(Account oldAcc, Account newAcc, boolean isCmr){

        if(isCmr){
            // For CMR's there's an old field called Client Code. We'll populate this field with the client number
            // however moving forward only the client number field should be used.
            if(oldAcc == null || newAcc.Client_Number__c!= oldAcc.Client_Number__c){
                newAcc.CMR_ClientCode__c = newAcc.Client_Number__c; 
            }           
        }               
    }
    
    private static void RefreshNasaIDs(List<Account> accounts, boolean isCmr){
        
        Set<ID> agencyIds = new Set<ID>();
        
        for(Account acc : accounts){
            if(isCmr){
                if(null != acc.Agency__c)
                    agencyIds.add(acc.Agency__c);
            }
            else{
                if(null != acc.AAS_Agency_Name__c )
                    agencyIds.add(acc.AAS_Agency_Name__c);
            }
        }
        
        // Get the parent agency information
        Map<ID, Account> agencies = new Map<ID, Account>([Select ID, Agency_ID__c from Account where ID in :agencyIds]);
        
        for(Account acc : accounts){
            
            if(acc.Client_Number__c != null){
                ID agencyID;
                                
                if(isCmr)
                    agencyID = acc.Agency__c;
                else
                    agencyID = acc.AAS_Agency_Name__c;
               
                if(agencies.containsKey(agencyID)){
                    acc.NASA_ID__c = agencies.get(agencyID).Agency_ID__c + '-' + acc.Client_Number__c;                    
                }
                else          
                    acc.NASA_ID__c = null;
            }
            else
                acc.NASA_ID__c = null;
                
            acc.CMR_Client_Code_Text__c = acc.NASA_ID__c;
        }
    }

    private static boolean IsValidNumber(string strNumber, integer minNumDigits, integer maxNumDigits){
        integer num = 0; 
        
        try
        {
            if(strNumber == null)
                return false;
                
            num = integer.valueof(strNumber);
            strNumber = strNumber.trim();
            
            if(minNumDigits >= 0 && strNumber.length() < minNumDigits)
                return false;

            if(maxNumDigits >= 0 && strNumber.length() > maxNumDigits)
                return false;
        }
        catch(Exception e)
        {
            return false;
        }
        
        return true;
    }
        
    private Class AgencyClientNumberGenerator{
        public Set<ID> masterClients;
        public List<Account> clients;
        
        public AgencyClientNumberGenerator(){
            masterClients = new Set<ID>();
            clients = new List<Account>();
        }
        
        public void QueueForClientNumberGeneration(Account client){
            if(client.Master_Client__c != null){            
                masterClients.add(client.Master_Client__c);
                clients.add(client);
            }
        }
        
        public void GenerateClientNumbers(){
            Map<ID, Account> mcMap = new Map<ID, Account>([Select ID, Master_Client_ID__c from Account where ID in :masterClients]);
            
            for(Account client:clients){
                client.Client_Number__c = mcMap.get(client.Master_Client__c).Master_Client_ID__c;
            }
        }
    }

    private static string PrefixString(string source, string prefixChar, integer minLength){
        string result;
        
        if(null != source)
            result = source;
        else
            result = '';
        
        while(result.length() < minLength){
            result= prefixChar + result;
        }
        
        return result;
    }
}